package com.mspbots.teams.sync.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mspbots.api.RestMessageObject;
import com.mspbots.api.client.CoreClient;
import com.mspbots.teams.model.ExceptionLog;
import com.mspbots.teams.model.TeamsUser;
import com.mspbots.teams.model.TenantDTO;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 *
 */
@Log4j2
@Service
public class TeamsSyncService {

    @Autowired
    protected ObjectMapper objectMapper;
    @Autowired
    protected CoreClient coreClient;
    @Autowired
    protected RabbitMessagingTemplate messagingTemplate;

    protected Mono<TenantDTO> onGetTenant(Integer tenantId) {
        return this.coreClient.get("/tenant/" + tenantId + "/info", null, TenantDTO.class);
    }

    public Mono<Object> autoSyncUsers(Integer tenantId) {

        TeamsGetClient teamsGetClient = new TeamsGetClient();
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.set("includingPhoto", "false");
        params.set("includingManager", "true");

        return onGetTenant(tenantId)
                .filter(tenantDTO -> tenantDTO.getStatusId() == 1)
                .flatMap(tenant -> {
                    if (StringUtils.isEmpty(tenant.getMicrosoftId())) {
                        this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                                .tenantId(tenantId).name("Teams")
                                .status(500).type("SYNC-TEAMS-USER").msg("no microsoft id")
                                .build());
                        return Mono.just(RestMessageObject.ok("Please check microsoft id"));
                    } else {
                        params.set("tenantId", tenant.getMicrosoftId());
                        log.debug("autoSyncUsers : {}", params);
                        return this.syncUsersProgress(teamsGetClient, params, tenantId);
                    }

                });
    }

    private Mono<Object> syncUsersProgress(TeamsGetClient teamsGetClient, MultiValueMap<String, String> params, Integer tenantId) {
        return teamsGetClient.getUserRepository(params).doOnNext(result -> {
            log.debug("teams user sync result : {}", result);
            String logMsg = "Teams user sync success.";
            if (!StringUtils.isEmpty(result)) {
                List<JsonNode> users = this.objectMapper.convertValue(result,
                        new TypeReference<List<JsonNode>>() {
                        });
                List<TeamsUser> teamsUserList = users.parallelStream().map(user -> {
                    TeamsUser teamsUser = new TeamsUser();
                    teamsUser.setTenantId(tenantId);
                    teamsUser.setTeamsUserId(user.get("id").asText());
                    teamsUser.setTeamsDisplayName(user.get("displayName").asText(null));
                    teamsUser.setTeamsMail(user.get("mail").asText(null));
                    teamsUser.setTeamsManager(user.get("manager").asText(null));
                    teamsUser.setMobilePhone("null".equalsIgnoreCase(user.get("mobilePhone").asText()) ? null : user.get("mobilePhone").asText(null));
                    if (ObjectUtils.isEmpty(user.get("businessPhone"))) {
                        AtomicReference<String> businessPhone = new AtomicReference<>("");
                        user.get("businessPhone").forEach(item -> businessPhone.updateAndGet(v -> v + item));
                        teamsUser.setBusinessPhone(businessPhone.toString());
                        log.debug("businessPhone {}", businessPhone.toString());
                    }

                    teamsUser.setJobTitle(user.get("jobTitle").asText(null));
                    teamsUser.setOfficeLocation("null".equalsIgnoreCase(user.get("officeLocation").asText(null)) ? null : user.get("officeLocation").asText(null));
                    teamsUser.setUpdateTime(LocalDateTime.now());
//                    this.messagingTemplate.convertAndSend("msp.teams.sync","teams-sync-users", teamsUser);

                    return teamsUser;

                }).collect(Collectors.toList());
                this.coreClient.postFlux("/teams/user/add-batch/" + tenantId, teamsUserList, TeamsUser.class).subscribe();
                log.info("tenant {} teams user size : {}", params.get("tenantId").get(0), users.size());
                logMsg = "Teams user sync " + users.size() + " success.";
            } else {
                log.info("tenant {} has no teams user !!!", tenantId);
                logMsg = "No teams user!";
                this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                        .tenantId(tenantId).name("Teams")
                        .status(500).type("SYNC-TEAMS-USER").msg(logMsg)
                        .build());
            }

        }).onErrorResume(e -> {
            log.error(e.getMessage());
            log.error(e.getSuppressed()[0].getMessage());

            this.messagingTemplate.convertAndSend("exception.log", ExceptionLog.builder()
                    .tenantId(tenantId).name("Teams")
                    .status(500).type("SYNC-TEAMS-USER").msg(e.getMessage()).extend(objectMapper.createObjectNode().put("detail", e.getSuppressed()[0].getMessage()))
                    .build());
            return Mono.error(e);
        })
                .map(result -> RestMessageObject.ok("Teams user sync success"));
    }
}
